from __future__ import annotations

"""
TradingView Webhook 路由（tv）
-----------------------------
职责：
- 接收并校验来自 TradingView 的信号
- 进行速率限制、去重与审计记录
- 将信号投递至后台工作器进行异步处理
"""

import secrets

import json

from fastapi import APIRouter, Body, Depends, HTTPException, Query, Request, status
from pydantic import ValidationError
from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession

from ..config import get_settings
from ..db import get_session
from ..deps import require_admin
from ..models import AuditLog, Script, ScriptSignal, ScriptStatus, SignalStatus
from ..schemas import RecentSignal, WebhookAcceptedResponse, WebhookSignalRequest
from ..services.rate_limit import script_rate_limiter
from ..services.signal_worker import signal_worker
from ..services.utils import hash_token, now_utc

router = APIRouter(prefix="/webhooks", tags=["tradingview"])


@router.post(
    "/tv/script/{webhook_id}",
    response_model=WebhookAcceptedResponse,
    status_code=status.HTTP_202_ACCEPTED,
    summary="TradingView Webhook 接收",
    description=(
        "接收来自 TradingView 的告警消息，校验 URL token 与 body secret 后直接触发下单（异步）。\n\n"
        "请求体字段说明：\n"
        "- secret：脚本创建时返回的 body_secret 明文；\n"
        "- nonce：唯一请求标识（时间戳/UUID），用于幂等；\n"
        "- trading_pair：交易对（如 XNA-USDT）；\n"
        "- target：下单资金配置（如 {mode: fixed_quote, value: 10}）；\n"
        "- order_type：MARKET/LIMIT（大小写不敏感）；\n"
        "- trade_type：BUY/SELL（大小写不敏感）；\n"
        "- position_action：OPEN/CLOSE（大小写不敏感）；\n"
        "- price：限价价格（市价可为空）。"
    ),
)
async def tradingview_webhook(
    webhook_id: str,
    request: Request,
    payload: WebhookSignalRequest = Body(
        ...,
        example={
        "nonce": "your-unique-nonce",
        "secret": "BodySecretFromScriptResponse",
        "trading_pair": "XNA-USDT",
        "target": {"mode": "fixed_quote", "value": 10},
        "order_type": "MARKET",
        "trade_type": "BUY",
        "position_action": "OPEN"
    },
    ),
    k: str = Query(..., alias="k"),
    session: AsyncSession = Depends(get_session),
) -> WebhookAcceptedResponse:
    """接收并校验 TradingView webhook。

    - 校验 URL token（k）与 body secret
    - 限流与状态检查，通过后写入 `script_signals` 并入队处理
    - 返回 202 接收成功与 signal_id
    """
    stmt = select(Script).where(Script.webhook_id == webhook_id)
    result = await session.execute(stmt)
    script = result.scalar_one_or_none()
    if not script:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Script not found")

    token_hash = hash_token(k)
    if not secrets.compare_digest(token_hash, script.url_token_hash):
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Invalid token")

    if not secrets.compare_digest(hash_token(payload.secret), script.body_secret_hash):
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Invalid secret")

    if script.status != ScriptStatus.active:
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Script paused")

    allowed = await script_rate_limiter.allow(
        f"script:{script.id}",
        get_settings().webhook_rate_per_min,
    )
    if not allowed:
        raise HTTPException(status_code=status.HTTP_429_TOO_MANY_REQUESTS, detail="Rate limited")

    payload_dump = json.loads(payload.model_dump_json())
    payload_dump["content_type"] = request.headers.get("content-type")

    signal = ScriptSignal(
        script_id=script.id,
        nonce=payload.nonce,
        payload_json=payload_dump,
        status=SignalStatus.accepted,
    )
    session.add(signal)
    session.add(
        AuditLog(
            actor_id=None,
            action="signal.received",
            target=f"script:{script.id}",
            payload_json=payload_dump,
        )
    )
    try:
        await session.commit()
    except IntegrityError:
        await session.rollback()
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="重复的 nonce（幂等拦截）")
    await session.refresh(signal)

    # 直接异步处理：队列消费逻辑会完成“符号一致性、订阅过滤、名义金额计算与下单”
    await signal_worker.submit(signal.id)
    return WebhookAcceptedResponse(accepted=True, signal_id=signal.id, message="信号已接收，后台处理中")


@router.get(
    "/tv/recent-signals",
    response_model=list[RecentSignal],
    summary="最近信号（管理员）",
    description="查看最近 50 条接收的 TradingView 信号及处理状态。",
)
async def recent_signals(
    admin=Depends(require_admin),
    session: AsyncSession = Depends(get_session),
) -> list[RecentSignal]:
    """管理员查看最近接收的信号记录（最多 50 条）。"""
    stmt = select(ScriptSignal).order_by(ScriptSignal.created_at.desc()).limit(50)
    result = await session.execute(stmt)
    signals = result.scalars().all()
    return [RecentSignal.from_orm(sig) for sig in signals]
